#include "../inc/uart.h"

extern struct termios old_cfg; //用于保存终端的配置参数
extern int fd;                 //串口终端对应的文件描述符
int rwflag = 0;
int readflag = 0;
unsigned char buf[10] = {0};
int j;

extern int w_buf[4]; //通过串口发送出去的数据

/**
** 串口初始化操作
** 参数 device 表示串口终端的设备节点
**/
int uart_init(const char *device)
{
    /* 打开串口终端 */
    fd = open(device, O_RDWR | O_NOCTTY);
    if (0 > fd)
    {
        fprintf(stderr, "open error: %s: %s\n", device, strerror(errno));
        return -1;
    }

    /* 获取串口当前的配置参数 */
    if (0 > tcgetattr(fd, &old_cfg))
    {
        fprintf(stderr, "tcgetattr error: %s\n", strerror(errno));
        close(fd);
        return -1;
    }

    return 0;
}

/**
** 串口配置
** 参数 cfg 指向一个 uart_cfg_t 结构体对象
**/
int uart_cfg(const uart_cfg_t *cfg)
{
    struct termios new_cfg = {0}; //将 new_cfg 对象清零
    speed_t speed;

    /* 设置为原始模式 */
    cfmakeraw(&new_cfg);
    /* 使能接收 */
    new_cfg.c_cflag |= CREAD;

    /* 设置波特率 */
    switch (cfg->baudrate)
    {
    case 1200:
        speed = B1200;
        break;
    case 1800:
        speed = B1800;
        break;
    case 2400:
        speed = B2400;
        break;
    case 4800:
        speed = B4800;
        break;
    case 9600:
        speed = B9600;
        break;
    case 19200:
        speed = B19200;
        break;
    case 38400:
        speed = B38400;
        break;
    case 57600:
        speed = B57600;
        break;
    case 115200:
        speed = B115200;
        break;
    case 230400:
        speed = B230400;
        break;
    case 460800:
        speed = B460800;
        break;
    case 500000:
        speed = B500000;
        break;
    default: //默认配置为 115200
        speed = B115200;
        printf("default baud rate: 115200\n");
        break;
    }

    if (0 > cfsetspeed(&new_cfg, speed))
    {
        fprintf(stderr, "cfsetspeed error: %s\n", strerror(errno));
        return -1;
    }

    /* 设置数据位大小 */
    new_cfg.c_cflag &= ~CSIZE; //将数据位相关的比特位清零
    switch (cfg->dbit)
    {
    case 5:
        new_cfg.c_cflag |= CS5;
        break;
    case 6:
        new_cfg.c_cflag |= CS6;
        break;
    case 7:
        new_cfg.c_cflag |= CS7;
        break;
    case 8:
        new_cfg.c_cflag |= CS8;
        break;
    default: //默认数据位大小为 8
        new_cfg.c_cflag |= CS8;
        printf("default data bit size: 8\n");
        break;
    }

    /* 设置奇偶校验 */
    switch (cfg->parity)
    {
    case 'N': //无校验
        new_cfg.c_cflag &= ~PARENB;
        new_cfg.c_iflag &= ~INPCK;
        break;
    case 'O': //奇校验
        new_cfg.c_cflag |= (PARODD | PARENB);
        new_cfg.c_iflag |= INPCK;
        break;
    case 'E': //偶校验
        new_cfg.c_cflag |= PARENB;
        new_cfg.c_cflag &= ~PARODD; /* 清除 PARODD 标志，配置为偶校验 */
        new_cfg.c_iflag |= INPCK;
        break;
    default: //默认配置为无校验
        new_cfg.c_cflag &= ~PARENB;
        new_cfg.c_iflag &= ~INPCK;
        printf("default parity: N\n");
        break;
    }
    /* 设置停止位 */
    switch (cfg->sbit)
    {

    case 1: //1 个停止位
        new_cfg.c_cflag &= ~CSTOPB;
        break;
    case 2: //2 个停止位
        new_cfg.c_cflag |= CSTOPB;
        break;
    default: //默认配置为 1 个停止位
        new_cfg.c_cflag &= ~CSTOPB;
        printf("default stop bit size: 1\n");
        break;
    }
    /* 将 MIN 和 TIME 设置为 0 */
    new_cfg.c_cc[VTIME] = 0;
    new_cfg.c_cc[VMIN] = 0;
    /* 清空缓冲区 */
    if (0 > tcflush(fd, TCIOFLUSH))
    {
        fprintf(stderr, "tcflush error: %s\n", strerror(errno));
        return -1;
    }
    /* 写入配置、使配置生效 */
    if (0 > tcsetattr(fd, TCSANOW, &new_cfg))
    {
        fprintf(stderr, "tcsetattr error: %s\n", strerror(errno));
        return -1;
    }
    /* 配置 OK 退出 */
    return 0;
}

/**
** 信号处理函数，当串口有数据可读时，会跳转到该函数执行
**/
void io_handler(int sig, siginfo_t *info, void *context)
{

    int ret;
    int n;
    if (SIGRTMIN != sig)
        return;
    /* 判断串口是否有数据可读 */
    if (POLL_IN == info->si_code)
    {
        ret = read(fd, buf, 8); //一次最多读 8 个字节数据
        printf("[ ");
        for (n = 0; n < ret; n++)
            printf("0x%hhx ", buf[n]);
        printf("]\n");
    }
    readflag = 1;
}
/**
** 异步 I/O 初始化函数
**/
void async_io_init(void)
{
    struct sigaction sigatn;
    int flag;
    /* 使能异步 I/O */
    flag = fcntl(fd, F_GETFL); //使能串口的异步 I/O 功能
    flag |= O_ASYNC;
    fcntl(fd, F_SETFL, flag);
    /* 设置异步 I/O 的所有者 */
    fcntl(fd, F_SETOWN, getpid());
    /* 指定实时信号 SIGRTMIN 作为异步 I/O 通知信号 */
    fcntl(fd, F_SETSIG, SIGRTMIN);
    /* 为实时信号 SIGRTMIN 注册信号处理函数 */
    sigatn.sa_sigaction = io_handler; //当串口有数据可读时，会跳转到 io_handler 函数
    sigatn.sa_flags = SA_SIGINFO;
    sigemptyset(&sigatn.sa_mask);
    sigaction(SIGRTMIN, &sigatn, NULL);
}

void writedata(void)
{
    system("himm 0x111f0000 0x0110");
    system("himm 0x111f0004 0x0110");
    // HI_MPI_SYS_SetReg(0x200f0078, 0x0);
    // write(fd, w_buf, 8); //一次向串口写入 8 个字节
    // sleep(1);            //间隔 1 秒钟

    for (j = 0; j < 4; j++)
    {
        write(fd, w_buf + j, 1);
        sleep(0.5);
        printf("send:%d\n", *(w_buf + j));
    }
    system("himm 0x111f0000 0x0210");
    system("himm 0x111f0004 0x0210");
    return;
}

void readdata(void)
{

    async_io_init(); //我们使用异步 I/O 方式读取串口的数据，调用该函数去初始化串口的异步 I/O
    while (readflag != 1)
        sleep(1); //进入休眠、等待有数据可读，有数据可读之后就会跳转到 io_handler()函数
}

int datahandle()
{
    if (buf[0] == 0x1)
        return 1;
    else if (buf[0] == 0x20)
    {
        printf("\r\n data_error \r\n");
        return 2;
    }

    return 0;
}

int read_write(void)
{
    if (rwflag == 0)
    {
        writedata();
        sleep(1);
        rwflag = 1;
    }
    if (rwflag == 1)
    {
        readdata();
        if (datahandle() == 1)
        {
            rwflag = 0;
            readflag = 0;
            return 1;
        }
        else if (datahandle() == 2)
        {
            rwflag = 0;
            readflag = 0;

            while (read_write() != 1)
                ;
            return 1;
        }
        rwflag = 0;
    }
    return 0;
}

void my_uart_init(void)
{
    uart_cfg_t cfg = {0};
    char *device = NULL;
    int rw_flag = -1;

    int n;

    device = "/dev/ttyAMA1";

    if (NULL == device)
    {
        fprintf(stderr, "Error: the device and read|write type must be set!\n");
        //show_help(argv[0]);
        exit(EXIT_FAILURE);
    }
    /* 串口初始化 */
    if (uart_init(device))
        exit(EXIT_FAILURE);
    /* 串口配置 */
    if (uart_cfg(&cfg))
    {
        tcsetattr(fd, TCSANOW, &old_cfg); //恢复到之前的配置
        close(fd);
        exit(EXIT_FAILURE);
    }
}